#include <system/Log.h>

namespace ne
{
    const char * const Log::LOG_LEVEL_MESSAGES[] = {"DEBUG",
                                                    "LOG",
                                                    "WARNING",
                                                    "ERROR"};

#ifndef NE_DEBUG
    Log Log::DefaultLog("NuclearEngine.log");
#else
    Log Log::DefaultLog("NuclearEngine_d.log");
#endif

    Log::Log(const std::string &fileName,
             const LogLevel level)
        : mFileName(fileName),
          mLevel(level),
          mTimeFormat("%Y.%m.%d %X")
    {
    }

    Log::~Log()
    {
    }

    const std::string& Log::getFileName() const
    {
        return mFileName;
    }

    void Log::setLevel(const LogLevel level)
    {
        mLevel = level;
    }

    LogLevel Log::getLevel() const
    {
        return mLevel;
    }

    void Log::setTimeFormat(const std::string &format)
    {
        mTimeFormat = format;
    }

    const std::string& Log::getTimeFormat() const
    {
        return mTimeFormat;
    }

    bool Log::open()
    {
        MutexLocker locker(&mMutex);
        FILE *file = fopen(mFileName.c_str(), "w");
        if (!file) return false;
        fprintf(file,
            "============================================================\n"
            "|                                                          |\n"
            "|  /\\  /\\              Nuclear Engine                      |\n"
            "| /__\\/__\\               Log System                        |\n"
            "|    /\\            Code by Rockie X.Lee                    |\n"
            "|   /__\\  Log Begin Time: %s         |\n"
            "|                                                          |\n"
            "============================================================\n",
            Timer::GetTimeString().c_str());
        fclose(file);
        return true;
    }

    bool Log::close()
    {
        MutexLocker locker(&mMutex);
        FILE *file = fopen(mFileName.c_str(), "a");
        if (!file) return false;
        fprintf(file,
            "============================================================\n"
            "|                                                          |\n"
            "|                      Nuclear Engine              /\\  /\\  |\n"
            "|                        Log System               /__\\/__\\ |\n"
            "|                  Code by Rockie X.Lee              /\\    |\n"
            "|          Log End Time: %s   /__\\   |\n"
            "|                                                          |\n"
            "============================================================\n",
            Timer::GetTimeString().c_str());
        fclose(file);
        return true;
    }

    bool Log::out(const std::string &format, va_list args)
    {
        return out(mLevel, format, args);
    }

    bool Log::out(const LogLevel level, const std::string &format, va_list args)
    {
#ifndef NE_DEBUG
        if (level == LL_DEBUG)
        {
            return false;
        }
#endif

        MutexLocker locker(&mMutex);
        FILE *file = fopen(mFileName.c_str(), "a");
        if (!file) return false;

        // Log out time
        std::string timeString = Timer::GetTimeString(mTimeFormat) +
            "." + Timer::GetMillisecondsString();
        fprintf(file, "[%s] %s >> ", timeString.c_str(), LOG_LEVEL_MESSAGES[level]);

        // Log out args
        vfprintf(file, format.c_str(), args);

        fprintf(file, "\n");
        fclose(file);
        return true;
    }

    bool Log::out(const std::string &format, ...)
    {
        va_list args;
        va_start(args, format);
        bool res = out(format.c_str(), args);
        va_end(args);
        return res;
    }

    bool Log::out(const LogLevel level, const std::string &format, ...)
    {
        va_list args;
        va_start(args, format);
        bool res = out(level, format.c_str(), args);
        va_end(args);
        return res;
    }

    Log& Log::operator << (const std::string &message)
    {
        out(message);
        return *this;
    }

    bool Log::Out(const std::string &format, ...)
    {
        va_list args;
        va_start(args, format);
        bool res = DefaultLog.out(format.c_str(), args);
        va_end(args);
        return res;
    }

    bool Log::Out(const LogLevel level, const std::string &format, ...)
    {
        va_list args;
        va_start(args, format);
        bool res = DefaultLog.out(level, format.c_str(), args);
        va_end(args);
        return res;
    }
}
